package cn.edu.lcu.cs.javaprogramming.db;

import cn.edu.lcu.cs.javaprogramming.util.StringUtils;
import lombok.extern.java.Log;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;

/**
 * 自编的结果集处理器，将结果集里的数据转置到一个JavaBean中。
 *
 * @param <T>
 * @author Ling
 */
@Log
public class BeanHandler<T> {
    private Class<? extends T> type;

    public BeanHandler(Class<? extends T> type) {
        super();
        this.type = type;
    }

    /**
     * 将结果集里的数据转置到一个JavaBean中
     *
     * @param rs
     * @return
     */
    public T handle(ResultSet rs) throws SQLException {
        T t = null;

        if (rs != null && !rs.isClosed()) {
            // 获取结果集元数据
            ResultSetMetaData metaData = rs.getMetaData();
            if (rs.next()) {
                // 创建一个新实例
                try {
                    t = type.getDeclaredConstructor().newInstance();
                } catch (InstantiationException | IllegalAccessException | InvocationTargetException |
                         NoSuchMethodException e) {
                    log.warning("创建实例失败：" + type.getName());
                    return null;
                }
                // 获取结果集列数
                int columnCount = metaData.getColumnCount();
                // 循环处理每一列，将列值赋值给对象的属性
                for (int i = 1; i <= columnCount; i++) {
                    // 获取列值
                    Object columnValue = rs.getObject(i);
                    // 获取列名（驼峰模式，或下划线模式）
                    String columnName = metaData.getColumnName(i);
                    // 把数据库编程规范中的下划线风格的列名转换为Java规范中的驼峰模式
                    // 如：map_under_score_to_camel_case --> MapUnderScoreToCamelCase
                    String camelCaseFieldName = StringUtils.mapUnderScoreToCamelCase(columnName);
                    // 首字母小写，即可变换为符合Java规范的Field名
                    camelCaseFieldName = StringUtils.toLowerCaseFirstLetter(camelCaseFieldName);
                    // 根据Field名称获取Field对象
                    Field field = null;
                    try {
                        field = type.getDeclaredField(camelCaseFieldName);
                    } catch (NoSuchFieldException e) {
                        log.warning(String.format("Field (%s) not found in Class (%s).", camelCaseFieldName, type.getName()));
                        // 获取字段失败，跳过后续，处理下一列。
                        continue;
                    }
                    // 设置Field可访问
                    field.setAccessible(true);
                    // 通过反射，设置对象t的field值
                    try {
                        field.set(t, columnValue);
                    } catch (IllegalArgumentException | IllegalAccessException e) {
                        log.warning(String.format("Set value (%s : %s) to bean field (%s : %s) error!", columnValue, columnValue.getClass().getName(), field.getName(), field.getType().getName()));
                        continue;
                    }
                }
            }
        }
        return t;
        //return null;
    }
}
